/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkClass, ArkFile, ViewTreeNode, ArkInvokeStmt, ArkAssignStmt } from 'arkanalyzer/lib';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { Rule, Defects, MatcherCallback, IssueReport, MatcherTypes, ViewTreeNodeMatcher } from '../../Index';

const gMetaData: BaseMetaData = {
    severity: 1,
    ruleDocPath: 'docs/custom-component-check.md',
    description: 'Avoid setting universal attributes for custom components.'
};

// 通用属性列表
const UNIVERSAL_ATTRS = [
    'width', 'height', 'size', 'padding', 'margin',
    'border', 'backgroundColor', 'opacity', 'visibility',
    'fontSize', 'fontColor', 'textAlign', 'type', 'objectFit',
    'placeholder', 'min', 'max', 'step', 'onClick', 'onTouch'
];

export class CustomComponentCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];

    private vtnMatcher: ViewTreeNodeMatcher = {
        matcherType: MatcherTypes.VIEW_TREE_NODE,
        match(node: ViewTreeNode): boolean {
            return node.isCustomComponent && node.isCustomComponent();
        }
    };

    public registerMatchers(): MatcherCallback[] {
        return [{ matcher: this.vtnMatcher, callback: this.check }];
    }

    public check = (node: ViewTreeNode): void => {
        const attr = UNIVERSAL_ATTRS.find(attr => this.shouldReportAttribute(node, attr));
        if (attr) {
            this.reportAttribute(node);
        }
    };

    private shouldReportAttribute(node: ViewTreeNode, attr: string): boolean {
        const stmts = node.attributes.get(attr);
        if (!Array.isArray(stmts) || stmts.length < 1) {
            return false;
        }
        const stmt = stmts[0];
        return stmt instanceof ArkInvokeStmt || stmt instanceof ArkAssignStmt;
    }

    private reportAttribute(node: ViewTreeNode): void {
        const stmts = node.attributes.get('pop');
        if (!Array.isArray(stmts) || stmts.length < 1) {
            return;
        }
        const stmt = stmts[0];
        const fullPosition = stmt.getOperandOriginalPositions()?.[0];
        if (!fullPosition) {
            return;
        }
        const line = fullPosition.getFirstLine();
        const startCol = fullPosition.getFirstCol();
        const endCol = fullPosition.getLastCol();
        const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
        if (!arkFile) {
            return;
        }
        this.reportIssue(line, startCol, endCol, arkFile.getFilePath());
    }

    private reportIssue(line: number, startCol: number, endCol: number, filePath: string): void {
        const severity = this.rule?.alert ?? this.metaData.severity;
        const defects = new Defects(
            line,
            startCol,
            endCol,
            this.metaData.description,
            severity,
            this.rule?.ruleId ?? 'custom-component-check',
            filePath,
            this.metaData.ruleDocPath,
            false,
            false,
            false
        );
        this.issues.push(new IssueReport(defects, undefined));
    }
}
